/* 

This package will be used in the election of a new primary
in the event of the failure of a node. 
   
*/

package main

// imports 
import (
	"fmt"	// used for printing
)

type primaryMapping struct {
	partitionNumber int
	previousPrimary string
	currentPrimary string
}

var myIndex int
var primaryList [100] primaryMapping
var peers [100] string
var crashedMap map[string] int = make(map[string] int)
var desiredConfiguration map[string] int = make(map[string] int)
var numServers int

func electionManager(index int, peerList [100] string, totalServers int) {
	peers = peerList
	myIndex = index
	numServers = totalServers
	for i := 0; i < numServers; i++ {
		var primary primaryMapping
		primary.partitionNumber = i
		primary.currentPrimary = peers[myIndex]
		desiredConfiguration[peers[i]] = i
		primaryList[i] = primary
		crashedMap[peers[i]] = 1 // currently unavailable
	}
	crashedMap[peers[myIndex]] = 0 // set current node to available 
	//printPrimaryConfiguration(primaryList)
	//fmt.Print("election manager initialized\n")
}

func electNewPrimary(crashedServer string) [100] primaryMapping {
	for i := 0; i < numServers; i++ {
		if primaryList[i].currentPrimary == crashedServer {
			// change value in crashed list
			crashedMap[crashedServer] = 1
			primaryList[i].previousPrimary = ""
			// select new current primary
			idealServer := primaryList[i].partitionNumber
			for j := 0; j < numServers; j++ {
			      	//fmt.Print("inside second loop\n")
				if crashedMap[peers[(idealServer + j) % numServers]] == 0 {
					primaryList[i].currentPrimary = peers[(idealServer + j) % numServers]
					//fmt.Print("Assigned ", primaryList[i].currentPrimary, " as new primary\n")
					break
				}
			}
		}
	}
	//printPrimaryConfiguration(primaryList)
	return primaryList
}

func restorePrimary(resurrectedServer string) [100] primaryMapping {
	desiredPartition := desiredConfiguration[resurrectedServer]
	for i := 0; i < numServers; i++ {
                if primaryList[i].partitionNumber == desiredPartition {
                        // change value in crashed list
                        crashedMap[resurrectedServer] = 0
			// set a previous and current mapping so data can be transferred
			primaryList[i].previousPrimary = primaryList[i].currentPrimary			
			primaryList[i].currentPrimary = resurrectedServer
			//fmt.Print("Changed partition ", primaryList[i].partitionNumber, " from ", primaryList[i].previousPrimary, " to ", primaryList[i].currentPrimary)
			break
                }
        }
	for i := 0; i < numServers; i++ {
	    if peers[i] != primaryList[i].currentPrimary {
	       idealServer := primaryList[i].partitionNumber	
	       for j := 0; j < numServers; j++ {
	       	   if crashedMap[peers[(idealServer + j) % numServers]] == 0 {
		      primaryList[i].previousPrimary = primaryList[i].currentPrimary
		      primaryList[i].currentPrimary = peers[(idealServer + j) % numServers]
		      //fmt.Print("Changed partition ", primaryList[i].partitionNumber, " from ", primaryList[i].previousPrimary, " to ", primaryList[i].currentPrimary, "\n")
		      break
		   }
	       }
	    }
	}
	//printPrimaryConfiguration(primaryList)
        return primaryList     
}

// debug function
func printPrimaryConfiguration(primaries [100] primaryMapping) {
     for i:= 0; i < numServers; i++ {
     	 fmt.Print("Partition Number: ", primaries[i].partitionNumber, " Primary: ", primaries[i].currentPrimary, "\n")
     }
}

/*func initListener(peer string){
     var ad net.TCPAddr
     ad.IP = net.ParseIP("localhost")
     ad.Port = 8888
     listen,e := net.ListenTCP("tcp", &ad)
     if e != nil {
     	fmt.Print("Election Manager Listener error: ", e.String())
     } 
     // any connections coming in on here are to request a vote
     for ; ; {
          conn,e := listen.AcceptTCP()
    	  if e != nil {
     	     fmt.Print("Election Manager accept error: ", e.String())
     	  } else {
             fmt.Print("Election Manager new connection with ", conn.RemoteAddr().String(), "\n")
	     go castVote(conn)
     	  }
     	  
     }
}

// process all votes received from other machines to select primary
func electNewPrimary(peersIP []string, peersVote []int) string {
     high := myVote
     highIndex = 0 //FIXME: this may need to be defaulted to the
     	       	    	     index of the peer the protocol is running on
     for i:=0; i < len(peersIP); i++{
     	 if high < peersVote[i]
	    high = peersVote[i]
	    highIndex = i;
     }
     return peersIP[highIndex]
}

// generate random number to decide who will be the next primary
func castVote(conn * net.TCPConn) {
     myVote := rand.Int()
     conn.Write(myVote)
}

// request the vote from another machine, using the machine number 
// may be IP in which case the parameter would be a string
func requestVote(conn * TCPConn){
     // TODO: make connect to other machine, if connection
     // times out, cast vote as -1. when all votes are in,
     // call electNewPrimary

     
}*/
